package com.lb.uts.client.controller;

import com.fasterxml.jackson.databind.util.JSONPObject;
import com.lb.uts.client.exception.UTSException;
import com.lb.uts.client.model.UtsClientConstants;
import com.lb.uts.client.util.AopTargetUtils;
import com.lb.uts.client.annotation.UtsTask;
import com.lb.uts.client.model.ResultBean;
import com.lb.uts.client.util.ScriptUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Conventions;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * uts-client 统一调用控制器<p/>
 * 将 Service 类的 Method 方法映射到URL: http://host:port/uts/service/method
 *
 * @author liangb
 */
@RestController
public class UTSController implements BeanFactoryAware {
    private static Logger logger = LoggerFactory.getLogger(UTSController.class);

    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    private BeanFactory context;

    /**
     * 根据给定的 service 和 method 名字, 查找对应Bean的method方法并执行<p/>
     * <p>
     * 将结果封装位统一的JSON串返回
     *
     * @param service
     * @param method
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/uts/{service}/{method}", method = {RequestMethod.POST, RequestMethod.GET})
    public Object gatewayInvoke(@PathVariable("service") String service, @PathVariable("method") String method, String jsonpCallback, HttpServletRequest request, HttpServletResponse response) {
        ResultBean result = new ResultBean();

        if (StringUtils.isBlank(service)) {
            throw new UTSException("UTS-00010", "service 不能为空");
        }

        if (StringUtils.isBlank(method)) {
            throw new UTSException("UTS-00010", "method 不能为空");
        }

        //判断是否是脚本
        if (service.startsWith(UtsClientConstants.SCRIPT_SERVICE) && method.startsWith(UtsClientConstants.SCRIPT_METHOD)) {
            try {
                ScriptUtil.exeScript(Integer.valueOf(request.getParameter(UtsClientConstants.SCRIPT_TYPE)), request.getParameter(UtsClientConstants.SCRIPT_PATH));
            } catch (Exception e) {
                logger.error("执行脚本失败:{}", e);
            }
            result.setResult(true);
            return wapperJsonp(jsonpCallback, result);
        } else {
            //从request中获取批量处理信息
            String batchPageNo = request.getParameter(UtsClientConstants.BATCH_PAGE_NO);
            String batchPageTotal = request.getParameter(UtsClientConstants.BATCH_PAGE_TOTAL);
            if (StringUtils.isNotBlank(batchPageNo) && StringUtils.isNotBlank(batchPageTotal)) {
                //放入ThreadLocal中
                Map<String, Object> batchMap = new HashMap<>();
                batchMap.put(UtsClientConstants.BATCH_PAGE_NO, batchPageNo);
                batchMap.put(UtsClientConstants.BATCH_PAGE_TOTAL, batchPageTotal);
                UtsClientConstants.put(batchMap);
            }
            //从容器中取得配置了UtsTask注解的service对象和方法
            Object bean = getBean(service);
            Method m = getMethod(bean, method);

            isSupport(request);

            HandlerMethod handler = new HandlerMethod(bean, m);
            ModelAndView mav = null;
            try {
                mav = requestMappingHandlerAdapter.handle(request, response, handler);
            } catch (UTSException e) {
                logger.error("UTS-00001", "系统调用异常:" + e.getMessage(), e);
                throw e;
            } catch (RuntimeException e) {
                logger.error("UTS-00001", "系统调用异常:" + e.getMessage(), e);
                throw e;
            } catch (Exception e) {
                logger.error("UTS-00001", "系统调用异常:" + e.getMessage(), e);
                throw new UTSException("UTS-00001", "系统调用异常:" + e.getMessage(), e);
            }

            result.setResult(false);
            Object value = null;
            if (mav.hasView()) {//返回数据基本类型及String 取值从view中获取、做特殊处理
                result.setResult(true);
                result.setData(mav.getViewName());
                return wapperJsonp(jsonpCallback, result);
            }
            if (mav.getModel().size() >= 1) {//返回数据是其他类型、取值从model中获取
                result.setResult(true);

                for (String key : mav.getModel().keySet()) {
                    value = mav.getModel().get(key);
                    try {
                        String realKey = Conventions.getVariableNameForReturnType(m, m.getReturnType(), value);
                        result.setData(mav.getModel().get(realKey));
                        break;
                    } catch (Exception e) {
                        logger.error("UTS-00001", "系统调用异常:" + e.getMessage(), e);
                        continue;
                    }
                }

                return wapperJsonp(jsonpCallback, result);
            }
            if (mav.getModel().size() == 0) {   //返回值为 null
                result.setResult(true);
                result.setData(null);
                return wapperJsonp(jsonpCallback, result);
            }
            result.setErrorCode("UTS-00000");
            result.setErrorMsg("系统处理错误");
            return wapperJsonp(jsonpCallback, result);
        }
    }

    private Object wapperJsonp(String function, ResultBean resultBean) {
        if (StringUtils.isNoneBlank(function)) {
            return new JSONPObject(function, resultBean);
        } else {
            return resultBean;
        }
    }

    private void isSupport(HttpServletRequest request) {
        //将当前上下文初始化给  CommonsMutipartResolver （多部分解析器）
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());

        //检查form中是否有enctype="multipart/form-data"
        if (multipartResolver.isMultipart(request)) {
            throw new UTSException("UTS-00005", "统一网关暂时不支持文件上传请求, 请另写Controller");
        }
    }

    private Method getMethod(Object bean, String methodName) {
        List<Method> list = new ArrayList<>();

        Method[] methods = bean.getClass().getMethods();
        for (Method method : methods) {
            if (!Modifier.isPublic(method.getModifiers())) {
                continue;
            }

            String name = method.getName();
            Method targetMethod = method;

            // 如果为代理对象, 取得代理的原始目标类型的方法
            if (AopUtils.isAopProxy(bean)) {
                targetMethod = AopTargetUtils.getTargetMethod(bean, name, method.getParameterTypes());
            }

            // 必须取得代理类的原始目标类的方法,才能拿到方法上的注解
            if (targetMethod != null && targetMethod.isAnnotationPresent(UtsTask.class)) {
                UtsTask gm = targetMethod.getAnnotation(UtsTask.class);

                if (StringUtils.equals(StringUtils.defaultIfBlank(gm.value(), name), methodName)) {
                    list.add(method);
                }
            }
        }

        if (CollectionUtils.isEmpty(list)) {
            throw new UTSException("UTS-00003", "method:" + methodName + "不是UTS方法");
        } else if (list.size() > 1) {
            throw new UTSException("UTS-00004", "method:" + methodName + "找到超过一个");
        }

        return list.get(0);
    }

    private Object getBean(String service) {
        try {
            return context.getBean(service);
        } catch (BeansException e) {
            logger.error("getBean err:", e);
            throw new UTSException("UTS-00002", "service:" + service + "不存在");
        }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.context = beanFactory;
    }
}
